//
//  PLInteraction.m
//  Plankton
//
//  Created by Marc Schlichte on 2008-07-17.
//  Copyright 2008 Framework Labs. All rights reserved.
//

#import "PLInteraction.h"

#import "PLPlatform.h"


@interface PLInteraction ()

@property(copy) NSNumber *iid;

@end


@implementation PLInteraction

@synthesize doneAction, timeout, timeoutAction;
@synthesize iid;

- (id)initWithAid:(NSString *)aid_ platform:(PLPlatform *)platform_ target:(id)target_
{
    if (self = [super init]) {
        aid = [aid_ copy];
        platform = [platform_ retain];
        target = [target_ retain];
        dict = [[NSMutableDictionary alloc] init];
        
        timeout = 10.0;
    }
    return self;
}

- (void)dealloc
{
    [dict release];
    [iid release];
    [target release];
    [platform release];
    [aid release];
    [super dealloc];
}

- (void)establishNewIid
{
    self.iid = [platform createTag];
}

- (id)valueForUndefinedKey:(NSString *)key
{
    return [dict objectForKey:key];
}

- (void)setValue:(id)val forUndefinedKey:(NSString *)key
{
    [dict setObject:val forKey:key];
}

@end


@interface PLMulticastRequestReplyInteraction ()

@property(retain) NSMutableSet  *peers;
@property(copy) NSString        *sender;
@property(retain) PLPattern     *receiveRequestsPattern;

- (void)checkIfDone;
- (NSString *)replyPerformative;

@end


@implementation PLMulticastRequestReplyInteraction

@synthesize receiveRequestAction, receiveReplyAction;
@synthesize peers, sender, receiveRequestsPattern;

+ (PLMulticastRequestReplyInteraction *)interactionForAid:(NSString *)aid 
                                             performative:(NSString *)perf
                                                 platform:(PLPlatform *)platform 
                                                   target:(id)target
{
    PLMulticastRequestReplyInteraction *res = [[PLMulticastRequestReplyInteraction alloc] 
                                               initWithAid:aid platform:platform target:target];
    res->performative = [perf copy];
    
    return [res autorelease];
}

- (void)dealloc
{
    [performative release];
    [receiveRequestsPattern release];
    [sender release];
    [peers release];
    [super dealloc];
}

- (void)sendRequests:(id)message to:(NSSet *)aids
{
    [self establishNewIid];
    
    self.peers = [aids mutableCopy];
    
    if ([peers count] == 0) {
        [self checkIfDone];
        return;
    }
    
    for (NSString *peer in [peers copy]) {
        [platform send:[NSArray arrayWithObjects:performative, iid, aid, message, nil] 
                    to:peer];
        
        PLPattern *pattern = [PLPattern patternWithPredicateFormat:@"SELF[0] = %@ AND SELF[1] = %@ AND SELF[2] = %@", 
                              [self replyPerformative], iid, peer];
        pattern.userInfo = peer;
        
        [pattern setMessageTarget:self action:@selector(onReceiveReply:pattern:)];
        [pattern setTimeoutTarget:self action:@selector(onReceiveReplyTimeout:) after:timeout];
        
        [platform receive:pattern];        
    }
}

- (void)receiveRequests
{
    self.receiveRequestsPattern = [PLPattern patternWithPredicateFormat:@"SELF[FIRST] = %@", performative];
    
    [receiveRequestsPattern setMessageTarget:self action:@selector(onReceiveRequests:pattern:)];
    
    [platform receive:receiveRequestsPattern];
}

- (void)sendReply:(id)message
{
    id msg = [NSArray arrayWithObjects:[self replyPerformative], iid, aid, message, nil];

    [platform send:msg to:sender];
    
    self.sender = nil;
}

- (void)onReceiveRequests:(id)message pattern:(PLPattern *)pattern
{
    self.iid = [message objectAtIndex:1];
    self.sender = [message objectAtIndex:2];
    
    [target performSelector:receiveRequestAction withObject:[message objectAtIndex:3] withObject:self];
    
    [platform receive:receiveRequestsPattern];
}

- (void)onReceiveReply:(id)message pattern:(PLPattern *)pattern
{
    [target performSelector:receiveReplyAction withObject:[message objectAtIndex:3] withObject:self];
    
    [peers removeObject:pattern.userInfo];
    [self checkIfDone];
}

- (void)onReceiveReplyTimeout:(PLPattern *)pattern
{
    NSString *peer = pattern.userInfo;
    [target performSelector:timeoutAction withObject:peer withObject:self];
    
    [peers removeObject:peer];
    [self checkIfDone];
}

- (void)checkIfDone
{
    if ([peers count] == 0 && doneAction) {
        [target performSelector:doneAction withObject:self];
    }
}

- (NSString *)replyPerformative
{
    return [performative stringByAppendingString:@"Reply"];
}

@end